package com.gitee.ley.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.ley.redis.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author liuenyuan
 **/
@Configuration
@EnableCaching
@Slf4j
public class RedisConfig extends CachingConfigurerSupport {

    @Autowired
    private RedisProperties redisProperties;

    public static final String REDIS_CACHE_NAME = "redis-cache-";

    /**
     * {@link org.springframework.cache.annotation.Cacheable}第一个注解代表从缓存中查询指定的key,如果有,从缓存中取,不再执行方法.如果没有则执
     * 行方法,并且将方法的返回值和指定的key关联起来,放入到缓存中.<br/>
     * {@link org.springframework.cache.annotation.CacheEvict}从缓存中清除指定的key对应的数据.<br/>
     * {@link org.springframework.cache.annotation.CachePut}在新增或者更新的时候,进行使用;更新时,则会覆盖原先的数据<br/>
     *
     * @see org.springframework.cache.annotation.CacheEvict
     * @see org.springframework.cache.annotation.Cacheable
     * @see org.springframework.cache.annotation.CachePut
     **/
    @Bean
    public CacheManager cacheManager(@Autowired RedisTemplate redisTemplate) {
        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
        //0表示永不过期
        redisCacheManager.setDefaultExpiration(0);
        return redisCacheManager;
    }

    /**
     * key redis serializer: {@link StringRedisSerializer} and
     * key redis serializer: {@link Jackson2JsonRedisSerializer}
     **/
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        Jackson2JsonRedisSerializer valueRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //设置Redis的value为json格式,并存储对象信息的序列化类型
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        valueRedisSerializer.setObjectMapper(objectMapper);

        RedisSerializer keyRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(keyRedisSerializer);
        template.setValueSerializer(valueRedisSerializer);
        template.setHashKeySerializer(keyRedisSerializer);
        template.setHashValueSerializer(valueRedisSerializer);
        template.setConnectionFactory(factory);
        template.afterPropertiesSet();

        log.info("redis server host:{},port:{},database:{}",
                redisProperties.getHost(), redisProperties.getPort(), redisProperties.getDatabase());
        return template;
    }

    @Bean
    public RedisUtils redisUtils(@Autowired RedisTemplate redisTemplate) {
        return new RedisUtils(redisTemplate);
    }
}
